<?php
/**
 * 
 * 目前支持以下验证类型 注意验证的提示信息中不能含有空格、换行和竖线和等号空格用{SP}代替，换行用{LF}代替，竖线用{VE}
 * string 字符,
 * time 时间日期,
 * alnum 字母加数字,
 * alpha 字母,
 * alnumu 字母数字下划线,
 * digits 数字,
 * graph 可显示的字符,
 * lower 小写,
 * print 可否打印,
 * punct 标点,
 * whitespace 空格或制表符,
 * upper 大写,
 * int 整型,
 * float 浮点型,
 * ipv4 ip地址,
 * binary 二进制数,
 * domain 域名,
 * email Email
 * 
 * 要验证的数组的key | 中文名称 | 验证类型 | 是否必须 | 其他(equal length size format)
 * title|标题|string=格式不正确|require=必须要填写|length(22:32)=长度不正确
 * age|年龄|int|require|size(1:200) equal(addtime)
 * addtime|时间|time|require|time_format(Y-m-d)
 * ipaddr|IP地址|ipv4|require|
 * ipv6|IPv6地址|ipv6|require|
 * email|email地址|email|require|
 * password|密码|string|require|equal(title)
 * a|测试a|float|require|length(0:199.99)
 * b|测试b|binary|require|
 * c|测试c|
 *
 */
class SP_Validator{
    
    var $_locale = null;
    var $break = false;
    var $pass = true;
    var $errmsg = array();
    var $arr = array();

    function validate($arr,$rule_str,$break = null){
        if($break !== null){
            $this->break = $break;
        }
        $this->arr = $arr;

        if(trim($rule_str) == ''){
            showError(__T('Validate rules can\'t be empty!'));
        }

        $rule_str = str_replace("\r",'',trim($rule_str));
        $rule_arr = explode("\n",$rule_str);

        foreach($rule_arr as $rule){
            if(!$this->_complier_rule($rule) && $this->break == true){
                return false;
            }
        }

        return $this->pass;
    }
    /**
     * 解析每条规则并验证
     */
    function _complier_rule($rule){
        $rule = trim($rule);

        if($rule == ''){
            showError(__T('Validate rules can\'t be empty!'));
        }else{
            $ruleargs = trimArray(explode('|',$rule));
            $args = array(
                'field'      => $ruleargs[0],
                'cname'      => $ruleargs[1],
                'type'       => $ruleargs[2],
                'require'    => $ruleargs[3],
                'expression' => $ruleargs[4]
            );
            unset($ruleargs);

            if($this->_validate_require($args)){ //验证字段是否必填
                $tpos = strpos($args['type'],'=');
                if($tpos){
                    $func_name = 'validate_'.substr($args['type'],0,$tpos);
                    $error_title = substr($args['type'],$tpos+1,strlen($args['type']));
                }else{
                    $func_name = 'validate_'.$args['type'];
                    $error_title = null;
                }
                if(method_exists($this,$func_name)){
                    $return = $this->{$func_name}($this->arr[$args['field']],$args,$error_title);
                }else{
                    //载入用户自定义的验证函数
                    Loader::helper("validate");
                    if(function_exists($func_name)){
                        list($return,$msg) = call_user_func($func_name,$this->arr[$args['field']],$args);

                        if(!$return){
                            if($tpos){
                                $this->_setMsg($args['field'],null,$error_title);
                            }else{
                                $this->_setMsg($args['field'],$args['cname'],$msg);
                            }
                        }
                    }else{
                        showError(__T('The validator function doesn\'t exist!'));
                    }
                }
                if(!$return){
                    $this->pass = false;
                }
                //验证表达式
                $this->_expressions($args);

            }

            return $this->pass;
        }
    }
    /**
     * 验证必填字段
     */
    function _validate_require($args){
        if( array_key_exists($args['field'],$this->arr) ){
            if(strncasecmp($args['require'],'require',7) == 0 && $this->arr[$args['field']] == ''){
                if($mpos = strpos($args['require'],'=')){
                    $this->_setMsg($args['field'],null,substr($args['require'],$mpos+1,strlen($args['require'])));
                }else{
                    $this->_setMsg($args['field'],$args['cname'],__T('Can\'t be empty!'));
                }
                $this->pass = false;
                return false;
            }elseif($this->arr[$args['field']] != ''){
                return true;
            }else{
                return false;
            }
        }elseif(strncasecmp($args['require'],'require',7) == 0){
            if($mpos = strpos($args['require'],'=')){
                $this->_setMsg($args['field'],null,substr($args['require'],$mpos+1,strlen($args['require'])));
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Can\'t be empty!'));
            }
            $this->pass = false;
            return false;
        }else{
            return false;
        }
    }
    /**
     * 验证表达式
     */
    function _expressions($args){
        if(preg_match_all('/([a-zA-Z0-9_-]+)\(([^\(]+)\)=?([^\s]*)/',$args['expression'],$t)){
            foreach($t[0] as $k=>$v){
                if($t[1][$k] != 'time_format'){
                    $tt = array('expression'=>$t[1][$k],'setting'=>$t[2][$k],'title'=>$t[3][$k]);
                    $equal_func = "_erun_".$t[1][$k];
                    if(method_exists($this,$equal_func)){
                        $this->{$equal_func}($args,$tt);
                    }else{
                        Loader::helper("validate");
                        if(function_exists($equal_func)){
                            list($return,$msg) = call_user_func($equal_func,$this->arr[$args['field']],$args,$tt);
                        }else{
                            showError(__T('The validator expression function doesn\'t exist!'));
                        }
                        if(!$return){
                            $this->pass = false;
                            if(!empty($tt['title'])){
                                $this->_setMsg($args['field'],null,$tt['title']);
                            }else{
                                $this->_setMsg($args['field'],$args['cname'],$msg);
                            }
                        }
                    }
                }
            }
        }
    }
    //表达式equal判断与某个字段是否相等
    function _erun_equal($args,$t){
        if($this->arr[$args['field']] != $this->arr[$t['setting']]){
            $this->pass = false;
            if(!empty($t['title'])){
                $this->_setMsg($args['field'],null,$t['title']);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Inconsistent!'));
            }
        }
    }
    //表达式length，判断字符长度
    function _erun_length($args,$t){
        if(strpos($t['setting'], ':') !== false){
            $length_rule = explode(':',$t['setting']);
            $length = strlen($this->arr[$args['field']]);

            if($length_rule[0] == '' && $length > $length_rule[1]){
                $this->pass = false;
                $title = sprintf(__T("Longer than the %s characters limit!"),$length_rule[1]);
            }
            if($length_rule[1] == '' && $length < $length_rule[0]){
                $this->pass = false;
                $title = sprintf(__T("Shall not be less than %s characters!"),$length_rule[0]);
            }
            if($length_rule[0] != '' && $length_rule[1] != '' && ($length < $length_rule[0] || $length > $length_rule[1])){
                $this->pass = false;
                $title = sprintf(__T("Must be between %s and %s characters!"),$length_rule[0],$length_rule[1]);
            }

            if(isset($title)){
                if(!empty($t['title'])){
                    $this->_setMsg($args['field'],null,$t['title']);
                }else{
                    $this->_setMsg($args['field'],$args['cname'],$title);
                }
            }
        }
    }
    //表达式size，验证数字型的大小,支持digits,int和float
    function _erun_size($args,$t){
        if(strpos($t['setting'], ':') !== false && preg_match('/^(digits|int|float)/i',$args['type'])){
            $size_rule = explode(':',$t['setting']);

            $num = $this->arr[$args['field']];

            if($size_rule[0] == '' && $num > $size_rule[1]){
                $this->pass = false;
                $title = sprintf(__T("Can't bigger than %s!"),$size_rule[1]);
            }
            if($size_rule[1] == '' && $num < $size_rule[0]){
                $this->pass = false;
                $title = sprintf(__T("Can't smaller than %s!"),$size_rule[0]);
            }
            if($size_rule[0] != '' && $size_rule[1] != '' && ($num < $size_rule[0] || $num > $size_rule[1])){
                $this->pass = false;
                $title = sprintf(__T("Must be between %s and %s!"),$size_rule[0],$size_rule[1]);
            }
            if(isset($title)){
                if(!empty($t['title'])){
                    $this->_setMsg($args['field'],null,$t['title']);
                }else{
                    $this->_setMsg($args['field'],$args['cname'],$title);
                }
            }
        }
    }
    function getMsg(){
        if($this->break){
            if(is_array($this->errmsg))
                return end($this->errmsg);
        }else{
            return $this->errmsg;
        }
    }

    function validate_string($value,$args,$typemsg = null){
        if(is_string($value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Must be a string!'));
            }
            return false;
        }
    }

    function validate_time($value,$args,$typemsg = null){
        $test = @strtotime($value);
        if ($test === false || $test === - 1)
        {
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Is not the correct time/date format!'));
            }
            return false;
        }

        if(preg_match('/time_format\(([^\(]+)\)=?([^\s]*)/',$args['expression'],$t)){
            if(date($t[1],$test) == $value){
                return true;
            }else{
                if(!empty($t[2])){
                    $this->_setMsg($args['field'],null,$t[2]);
                }else{
                    $this->_setMsg($args['field'],$args['cname'],__T('Is not the correct time/date format!'));
                }
                return false;
            }
        }
        return true;
    }

    function validate_alnum($value,$args,$typemsg = null)
    {
        if(ctype_alnum($value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Can only be number and letters!'));
            }
            return false;
        }
    }

    function validate_alpha($value,$args,$typemsg = null)
    {
        if(ctype_alpha($value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Must be a letter!'));
            }
            return false;
        }
    }

    function validate_alnumu($value,$args,$typemsg = null)
    {
        if(preg_match('/[^a-zA-Z0-9_]/', $value) == 0){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Can only be letters, numbers and underscores!'));
            }
            return false;
        }
    }

    function validate_digits($value,$args,$typemsg = null)
    {
        if(is_numeric($value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Must be a number!'));
            }
            return false;
        }
    }

    function validate_graph($value,$args,$typemsg = null)
    {
        if(ctype_graph($value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Characters must be visible!'));
            }
            return false;
        }
    }

    function validate_lower($value,$args,$typemsg = null)
    {
        if(ctype_lower($value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Must be all lowercase!'));
            }
            return false;
        }
    }

    function validate_print($value,$args,$typemsg = null)
    {
        if(ctype_print($value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Must be printable characters!'));
            }
            return false;
        }
    }

    function validate_punct($value,$args,$typemsg = null)
    {
        if(ctype_punct($value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Must be punctuation!'));
            }
            return false;
        }
    }

    function validate_whitespace($value,$args,$typemsg = null)
    {
        if(ctype_space($value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Must be spaces or tabs!'));
            }
            return false;
        }
    }

    function validate_upper($value,$args,$typemsg = null)
    {
        if(ctype_upper($value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Must be all uppercase!'));
            }
            return false;
        }
    }

    function validate_int($value,$args,$typemsg = null)
    {
        if (is_null($this->_locale))
        {
            $this->_locale = localeconv();
        }

        $value = str_replace($this->_locale['decimal_point'], '.', $value);
        $value = str_replace($this->_locale['thousands_sep'], '', $value);

        if (strval(intval($value)) != $value)
        {
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Must be an integer!'));
            }
            return false;
        }
        return true;
    }

    function validate_float($value,$args,$typemsg = null)
    {
        if (is_null($this->_locale))
        {
            $this->_locale = localeconv();
        }

        $value = str_replace($this->_locale['decimal_point'], '.', $value);
        $value = str_replace($this->_locale['thousands_sep'], '', $value);

        if (strval(floatval($value)) != $value)
        {
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Must be a float!'));
            }
            return false;
        }
        return true;
    }

    /**
     * 是否是 IPv4 地址（格式为 a.b.c.h）
     *
     * @param mixed $value
     *
     * @return boolean
     */
    function validate_ipv4($value,$args,$typemsg = null)
    {
        $test = @ip2long($value);
        if($test !== - 1 && $test !== false){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Is not a valid IP address!'));
            }
            return false;
        }
    }

    /**
     * 是否是二进制数值
     *
     * @param mixed $value
     *
     * @return boolean
     */
    function validate_binary($value,$args,$typemsg = null)
    {
        if(preg_match('/[01]+/', $value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Must be binary number!'));
            }
            return false;
        }
    }

    /**
     * 是否是 Internet 域名
     *
     * @param mixed $value
     *
     * @return boolean
     */
    function validate_domain($value,$args,$typemsg = null)
    {
        if(preg_match('/[a-z0-9\.]+/i', $value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Is not a valid domain name!'));
            }
            return false;
        }
    }

    function validate_email($value,$args,$typemsg = null)
    {
        if(preg_match('/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $value)){
            return true;
        }else{
            if(isset($typemsg)){
                $this->_setMsg($args['field'],null,$typemsg);
            }else{
                $this->_setMsg($args['field'],$args['cname'],__T('Is not a valid Email Address!'));
            }
            return false;
        }
    }

    function _setMsg($key,$name=null,$text){
        if(!isset($this->errmsg[$key])){
            if(!isset($name)){
                $this->errmsg[$key] = str_replace(array('{SP}','{LF}','{VE}'),array(' ',"\n",'|'),$text);
            }else{
                $this->errmsg[$key] = $name.': '.$text;
            }
        }
    }

}